package com.workday.gridsimulator

import akka.actor.Actor
import akka.actor.ActorRef
import akka.event.Logging
import scala.math.BigDecimal
import scala.math.BigDecimal

/**
 * Slave/Worker actor for processing work
 *
 * When allocated a job by the grid, it adds it to the Clocks agenda and waits for a ping back from the Clock when it advances.
 *
 * After a ping back from the Clock ( which signifies work completion), the worker sends a WorkCompleted message to the grid and remains available for more
 * work if needed.
 *
 * Ultimately the grid tells the worker when there is no more work and then the worker will tell the Clock it is finished and shut itself down.
 *
 * @param id the id of the worker
 *
 * @param grid the worker registers with this grid actor, is allocatted work by it and sends work completion messages to it etc.
 *
 * @param clock the clock actor the worker uses for adding to the agenda
 */
class SimpleJobWorker(id: Int, ec2InstanceType: Ec2Instance, grid: ActorRef, clock: ActorRef) extends Worker(id, ec2InstanceType, grid, clock) {

  private var currentWork: Work = null
  private var startTime = 0.0
  private var endTime = 0.0

  val log = Logging(context.system.eventStream, self.path.toString())

  override def receive = {
    
    case Start(currentTime) =>
      log.info("told to start at " + currentTime)
      startTime = currentTime
      
    case RegisterWithGrid =>
      log.info(this.id + " registering with grid")
      grid ! WorkerRegistration(this)

    case Stop =>
      log.info("told to stop by " + sender)
      context.stop(self)

    case AllocatedWork(currentTime, work: Work) =>
      log.info("was allocatted work id " + work.id + " at time " + currentTime)
      currentWork = work
      clock ! Hold((currentTime + currentWork.execTime), context.self)

    case Ping(currentTime) =>
      if (currentWork != null) {
        currentWork.endTime = currentTime
        log.info("finished work " + currentWork.id + " at time " + currentTime)
        grid ! WorkCompleted(currentTime, currentWork, this)
        currentWork = null
      }

    case NoMoreWork(currentTime) =>
      println("told clock it is finished")
      clock ! Finished(self)
      endTime = currentTime
      val duration = endTime - startTime
      
      if (ec2InstanceType != null) {
        val cost = calcCost(duration)
        grid ! WorkerCost(cost, currentTime)
      }
      
      else grid ! WorkerCost(0.0, currentTime)

    case WorkingOnWhat =>
      log.info("asked what i am working on by " + sender)
      sender ! currentWork

    case "StartTime" =>
      sender ! this.startTime
      
    case _ =>
      log.warning("unknown message received from " + sender)
  }

  private def calcCost(durationInSeconds: Double) = {

    var hours = durationInSeconds / 360
    val mins = durationInSeconds % 60

    if (mins != 0 || hours == 0) hours += 1 //amazon charge by the hour....if you use ec2instance for 5 hours 2 mins, get charged for 6 hours etc.
    
    var cost = hours * ec2InstanceType.ratePerHour
    
     //round up to 5 decimal digits
    BigDecimal(cost).setScale(5, BigDecimal.RoundingMode.HALF_UP).toDouble
    
  }
}